package com.example.nacosconsumer.service;

import com.alibaba.fastjson.JSON;
import com.example.nacosconsumer.dao.ForumMapper;
import com.example.nacosconsumer.pojo.tiezi;
import com.example.nacosconsumer.util.JsonResult;
import com.github.pagehelper.Page;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class ForumService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private ForumMapper forumMapper;
    /**
     * 签到服务函数
     * @return 本月签到信息
     */
    public String sign() {
        Map<String, String> massage = new HashMap<>();
        //1.从Authentication中获取当前用户的用户名
        String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();
        //2. 获取日期
        LocalDateTime now = LocalDateTime.now();
        //3. 拼接key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = currentUser + keySuffix;
        //4. 获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        //5. 写入redis setbit key offset 1
        stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
        Long days = getSignDays();  //连续签到天数
        Long count = getSignCount();  //本月签到总天数
        massage.put("days", String.valueOf(days));
        massage.put("count", String.valueOf(count));
        String msg = "签到成功!您本月累计签到" + count + "天，" + "本月已连续签到" + days + "天";
        return JSON.toJSONString(new JsonResult<>(msg));
    }

    /**
     * 获取用户当月签到总天数
     * @return
     */
    public long getSignCount() {
        //1.从Authentication中获取当前用户的用户名
        String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();
        //2. 获取日期
        LocalDateTime now = LocalDateTime.now();
        //3. 拼接key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = currentUser + keySuffix;
        return stringRedisTemplate.execute(
                (RedisCallback<Long>) con -> con.bitCount(key.getBytes()));
    }

    /**
     * 获取用户连续签到天数
     * @return
     */
    public long getSignDays() {
        //1.从Authentication中获取当前用户的用户名
        String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();
        //2. 获取日期
        LocalDateTime now = LocalDateTime.now();
        //3. 拼接key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = currentUser + keySuffix;
        //4. 获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        //5. 写入redis setbit key offset 1
        stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
        //6.获取当前用户本月签到天数(该月0号到当天的十进制bit数)
        List<Long> result = stringRedisTemplate.opsForValue().bitField(
                key,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        //
        Long num = result.get(0);
        //6. 循环遍历
        int count = 0;  //本月连续天数
        while (true) {
            //6.1 让这个数字与1 做与运算，得到数字的最后一个bit位 判断这个数字是否为0
            if ((num & 1) == 0) {
                //如果为0，签到结束
                break;
            } else {
                count ++;
            }
            num >>>= 1;  //无符号右移一位并赋值给num
        }
        return count;
    }

    /**
     * 点赞功能实现
     */
    public String Star(String tiezi_ID,String currentUser) {
        //返回信息
        String msg;
        //1.从Authentication中获取当前用户的用户名

        //2.根据帖子ID拼接key
        String key = "starInfo:tiezi:ID:" + tiezi_ID;
        //判断用户是否已点赞 是则取消点赞
        if (isMember(tiezi_ID,currentUser)==0) {
            //3.添加一个用户到点赞列表里面
            stringRedisTemplate.opsForSet().add(key, currentUser);
            //根据帖子ID更新帖子的点赞数、用于点赞数排名
            forumMapper.updateStarNum(tiezi_ID,starNum(tiezi_ID));
            //stringRedisTemplate.opsForZSet().incrementScore("tiezi:hotList", key, 1);
            msg = "点赞成功";
        }
        else {
            //从点赞列表中删除该用户的点赞
            stringRedisTemplate.opsForSet().remove(key, currentUser);
            //根据帖子ID更新帖子的点赞数、用于点赞数排名
            forumMapper.updateStarNum(tiezi_ID,starNum(tiezi_ID));
            //stringRedisTemplate.opsForZSet().incrementScore("tiezi:hotList", key, -1);
            msg = "点赞取消";
        }
        return JSON.toJSONString(new JsonResult<>(msg));
    }

    /**
     * 查询帖子的点赞数量
     * @param tiezi_ID 帖子ID 用于查询
     */
    public Long starNum(String tiezi_ID) {
        //1.根据帖子ID拼接key、用于后面查询
        String key = "starInfo:tiezi:ID:" + tiezi_ID;
        //2.根据key查询帖子的点赞数量
        return stringRedisTemplate.opsForSet().size(key);
    }

    /**
     * 查询用户对帖子的点赞状态
     * @param tiezi_ID 帖子ID
     * @return 返回0或1
     */
    public Integer isMember(String tiezi_ID,String currentUser) {
        //1.从Authentication中获取当前用户的用户名
        //String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();
        //2.根据帖子ID拼接key、用于后面查询
        String key = "starInfo:tiezi:ID:" + tiezi_ID;
        //3.根据key值查询该用户是否已点赞该帖子
        Boolean statu=stringRedisTemplate.opsForSet().isMember(key, currentUser);
        if (Boolean.TRUE.equals(statu)) {
            return 1;
        }
        else {
            return 0;
        }
    }

    /**
     * 获取排名前n名的热帖ID
     * @return 返回热帖ID列表
     */
    public List<Integer> hotRank() {
        //帖子点赞数量
        String key = "tiezi:hotList";
        //获取点赞数正序排名前n名的帖子列表
        Set<String> hotList=stringRedisTemplate.opsForZSet().reverseRange(key,0,2);
        assert hotList != null;  //断言hotList不为空
        //通过stream流将前缀替换掉只留下ID集合 如starInfo:tiezi:ID:帖子ID -->帖子ID
        List<Integer> hotIDList =hotList.stream().map(ID -> Integer.valueOf(ID.replace("starInfo:tiezi:ID:", ""))).collect(Collectors.toList());
        System.out.println(hotIDList);
        return hotIDList;
    }
}

